﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GnufBase.h"
#include "GnufUiCommonDef.h"

namespace {
    class CustomizedPreferenceEntryPlaceholder;
}

namespace gnuf {

    class Preferences;
    class PreferenceEntryGroup;
    class CustomizedPreferenceEntry;

    /// \brief CustomizedPreferenceEntryCreatorFunc intends to define a creator
    /// function.
    typedef std::function<
        gcmp::OwnerPtr<CustomizedPreferenceEntry>(PreferenceEntryGroup *)
    > CustomizedPreferenceEntryCreatorFunc;

    /// \brief PreferenceEntryNode class is the base class of PreferenceEntry 
    /// class and PreferenceEntryGroup class.
    class GNUF_BASE_EXPORT PreferenceEntryNode
        : public gcmp::WeakReferenceable
    {
        DEFINE_CAST_BASE(PreferenceEntryNode)
    public:
        explicit PreferenceEntryNode();
        ~PreferenceEntryNode();

        // Set/Get the key.
        void setKey(const std::wstring & val) { m_key = val; }
        const std::wstring & key() const { return m_key; }

        // Set/Get the display name.
        void setDisplayName(const std::wstring & val) { m_displayName = val; }
        const std::wstring & displayName() const { return m_displayName; }

    private:
        std::wstring m_key;
        std::wstring m_displayName;        
    };

    ///////////////////////////////////////////////////////////////////////////
    // PreferenceEntry(s)
    //
    class PreferenceEntrySnapshot;
    class GNUF_BASE_EXPORT PreferenceEntry : public PreferenceEntryNode
    {
        DEFINE_CAST_DERIVED(PreferenceEntry, PreferenceEntryNode)
    public:
        enum EType {
            eTheme,
            eBoolean,

            eCustomized
        };
        explicit PreferenceEntry(
            PreferenceEntryGroup *,
            PreferenceEntry::EType);
        virtual ~PreferenceEntry();

        /// \return Return the Preferences instance.
        Preferences * preferences() const;

        /// \return Return the preference entry group that handles this PreferenceEntry 
        /// instance.
        PreferenceEntryGroup * group() const { return m_group; }

        /// \return Return the type of the preference entry.
        PreferenceEntry::EType type() const { return m_type; }

        template <class TEntry>
        TEntry * cast()
        {
            if (TEntry::Type == m_type)
                return static_cast<TEntry*>(this);
            return nullptr;
        }

        template <class TEntry>
        const TEntry * cast() const
        {
            if (TEntry::Type == m_type)
                return static_cast<const TEntry*>(this);
            return nullptr;
        }

        ////////////////////////////////////////////////////////////////////////////////////////
        // Obsolete interface method. The GNUF will delete it, when get time. -frank 2023.05.22.
        template <class TEntry>
        const TEntry * constCast() const { return cast<TEntry>(); }
        ////////////////////////////////////////////////////////////////////////////////////////

    protected:
        /// \brief Make a snap shot for a PreferenceEntry and return it.
        friend class Preferences;
        virtual gcmp::OwnerPtr<PreferenceEntrySnapshot> makeSnapshot() = 0;

    private:
        PreferenceEntryGroup * m_group;
        const PreferenceEntry::EType m_type;
    };
    
    /// \brief PreferenceEntrySnapshot intends to make a snapshot for a preference
    /// entry, which is used to compare and write back the preference value.
    class GNUF_BASE_EXPORT PreferenceEntrySnapshot
    {
    public:
        explicit PreferenceEntrySnapshot(PreferenceEntry *);
        virtual ~PreferenceEntrySnapshot();
        const PreferenceEntry * entry() const { return m_entry.Get(); }
        /// \brief Compare with the snapshot.
        /// \return Return true if changed. Otherwise, return false.
        virtual bool isChanged() const = 0;
        /// \brief Write the value in the snapshot, back to the preference entry.
        virtual void writeBack() = 0;
    protected:
        PreferenceEntry * entry() { return m_entry.Get(); }
    private:
        gcmp::WeakPtr< PreferenceEntry > m_entry;
    };

    class GNUF_BASE_EXPORT ThemePreferenceEntry final : public PreferenceEntry
    {
        DEFINE_CAST_DERIVED(ThemePreferenceEntry, PreferenceEntry)
    public:
        static const PreferenceEntry::EType Type;
        static const std::wstring themeThumbnail(ETheme);
        explicit ThemePreferenceEntry(PreferenceEntryGroup *);
        virtual ~ThemePreferenceEntry();

        // Set/Get the theme.
        void setTheme(ETheme theme) { m_theme = theme; }
        ETheme theme() const { return m_theme; }

        // Set/Get the alert.
        void setAlert(const std::wstring & alert) { m_alert = alert; }
        const std::wstring & alert() const { return m_alert; }

    protected:
        // Inherits from PreferenceEntry...
        virtual gcmp::OwnerPtr<PreferenceEntrySnapshot> makeSnapshot() override;

    private:
        ETheme m_theme;
        std::wstring m_alert;
    };

    class GNUF_BASE_EXPORT BooleanPreferenceEntry final : public PreferenceEntry
    {
        DEFINE_CAST_DERIVED(BooleanPreferenceEntry, PreferenceEntry)
    public:
        static const PreferenceEntry::EType Type;
        explicit BooleanPreferenceEntry(PreferenceEntryGroup *);
        virtual ~BooleanPreferenceEntry();

        // Set/Get the value.
        void setValue(bool value) { m_value = value; }
        bool value() const { return m_value; }

    protected:
        // Inherits from PreferenceEntry...
        virtual gcmp::OwnerPtr<PreferenceEntrySnapshot> makeSnapshot() override;

    private:
        bool m_value;
    };
    
    /// \brief PreferenceEntryGroup class intends to handle a group of preference
    /// entries.
    class GNUF_BASE_EXPORT PreferenceEntryGroup final : public PreferenceEntryNode
    {
        CANNOT_COPY_OR_ASSIGN(PreferenceEntryGroup)
    public:
        explicit PreferenceEntryGroup(Preferences *);
        ~PreferenceEntryGroup();

        /// \return Return the Preferences instance that handles this group.
        Preferences * preferences() const { return m_pPreferences; }

        /// \brief Add a type of preference entry, through the type.
        /// \param type: The type of PreferenceEntry.
        /// \return Return the pointer to PreferenceEntry instance if successful.
        /// Return nullptr if failed.
        PreferenceEntry * addEntry(PreferenceEntry::EType type);
        
        /// \brief Add a customized preference entry, through the creator.
        /// \return Return the pointer to the CustomizedPreferenceEntry instance 
        /// if successful. Return nullptr if failed.
        CustomizedPreferenceEntry * addEntry(const CustomizedPreferenceEntryCreatorFunc & creator);

        /// \return Return the preference entry at the index.
        const PreferenceEntry * entryAt(const size_t index) const;
              PreferenceEntry * entryAt(const size_t index);

        const PreferenceEntry * entryBy(const PreferenceEntry::EType type) const;
              PreferenceEntry * entryBy(const PreferenceEntry::EType type);

        /// \return Return the preference entry by the key.
        const PreferenceEntry * entryBy(const std::wstring & key) const;
              PreferenceEntry * entryBy(const std::wstring & key);

        /// \brief Remove a preference entry whose key equals the given key.
        bool removeEntry(const std::wstring & key);

        ///\brief Clear those prefernece entries that the client doesn't need any more.
        void clearEntries(const std::function<bool(const PreferenceEntry *)> & wantToRemove);

        /// \brief Replace the placeholder. Once replaced, pPlaceholder 
        /// is not valid, any more.
        CustomizedPreferenceEntry * replacePlaceholder(
            const CustomizedPreferenceEntryPlaceholder * pPlacholder,
            const CustomizedPreferenceEntryCreatorFunc & creator);
        
        typedef std::list< gcmp::OwnerPtr< PreferenceEntry > > Entries;
        typedef std::vector< PreferenceEntry * > RawEntries;
        /// \return Return all handled PreferenceEntry(s).
        const PreferenceEntryGroup::Entries & entries() const { return m_entries; }
        PreferenceEntryGroup::RawEntries rawEntries();
        PreferenceEntryGroup::Entries & rEntries() { return m_entries; }

        /// \brief Set if visible.
        void setVisible(bool val) { m_visible = val; }
        /// \return Return ture if visible.
        bool isVisible() const { return m_visible; }

    private:
        Preferences * m_pPreferences = nullptr;
        PreferenceEntryGroup::Entries m_entries;
        bool m_visible = true;
    };
}